import { stringify } from 'qs'
import FormData from 'form-data'
import { Keys } from '@/constants'
import { useAuthStore } from '@/store'
import { isArray, isFile, isObject, localStg } from '@/utils'
import { REFRESH_TOKEN_CODE, RELOGIN_TOKEN_CODE } from '@/utils/request/constant'
import { Api } from '@/typings/request'
import { transformResultByBackendError } from '@/utils/request/transform'
import { JBoltApi } from '@/utils/request/index'
import type {
    AxiosInstance,
    AxiosResponse,
    AxiosResponseHeaders,
    InternalAxiosRequestConfig,
    RawAxiosResponseHeaders
} from 'axios'
import { getServiceEnvConfig } from '~/.env-config'

const { isApp, appId } = getServiceEnvConfig(import.meta.env)

/**
 * 接口为上传文件的类型时数据转换
 * @param key - 文件的属性名
 * @param file - 单文件或多文件
 */
function transformFile(formData: FormData, key: string, file: File[] | File) {
    if (isArray(file)) {
        ;(file as File[]).forEach((item) => {
            formData.append(key, item)
        })
    } else {
        // 单文件
        formData.append(key, file)
    }
}

/**
 * 处理表单数据
 * @param data
 */
function handleFormData(data: any) {
    if (data instanceof FormData) {
        return data
    }
    if (isObject(data) == false) {
        return data
    }
    const formData = new FormData()
    const entries = Object.entries(data)

    entries.forEach(async ([key, value]) => {
        const isFileType =
            isFile(value) || (isArray(value) && value.length && isFile(value[0]))

        if (isFileType) {
            transformFile(formData, key, value)
        } else {
            formData.append(key, value)
        }
    })

    return formData
}

/**
 * 根据contentType对数据做二次处理
 * @param data
 * @param contentType
 */
function transformRequestData(data: any, contentType: string) {
    if (contentType === 'application/x-www-form-urlencoded') {
        // 对于application/x-www-form-urlencoded，要对数据做二次处理
        return stringify(data)
    }

    if (contentType === 'multipart/form-data') {
        // form-data类型转换
        data = handleFormData(data)
    }
    return data
}

/**
 * 处理请求头
 * @param config
 */
function processRequestHeader(config: InternalAxiosRequestConfig) {
    config.headers[Keys.TOKEN] = localStg.get(Keys.TOKEN) || ''

    if (isApp) {
        config.headers['JBOLTAPI'] = 'true'
        config.headers[Keys.APPID] = appId
    }
}

/**
 * 请求拦截
 */
function interceptRequest(instance: AxiosInstance) {
    instance.interceptors.request.use(async function (
        config: InternalAxiosRequestConfig & Api.RequestConfig
    ) {
        if (config.skipInterceptor || config.skipRequestInterceptor) {
            return config
        }
        const contentType =
            config.headers?.['Content-Type'] || config.headers?.['content-type']
        if (config.data) {
            config.data = transformRequestData(config.data, contentType)
        }

        //处理JBolt特有的header属性
        processRequestHeader(config)

        return config
    })
}

/**
 * 处理响应头
 * @param headers
 */
function processResponseHeader(headers: AxiosResponseHeaders) {
    const jboltjwt = headers[Keys.TOKEN]
    if (jboltjwt) {
        localStg.set(Keys.TOKEN, jboltjwt, null)
    }
    const jboltrefreshjwt = headers[Keys.REFRESH_TOKEN]
    if (jboltrefreshjwt) {
        localStg.set(Keys.REFRESH_TOKEN, jboltrefreshjwt, null)
    }
}

/**
 * 刷新状态
 */
const refreshState: {
    isRefreshing: boolean
    queue: any[]
} = {
    isRefreshing: false,
    queue: [] //刷新后等待重新发起的请求
}

/**
 * 执行刷新token
 */
async function executeRefreshToken(): Promise<Api.Result> {
    const { resetAuthStore } = useAuthStore()
    const _refreshToken = localStg.get(Keys.REFRESH_TOKEN) || ''
    const result = await JBoltApi.get('/api/jwt/refresh', null, {
        headers: {
            [Keys.REFRESH_TOKEN]: _refreshToken
        }
    })
    if (result?.error) {
        resetAuthStore()
    }
    return result
}

/**
 * 刷新token
 * @param instance
 * @param requestConfig
 */
function waitRefreshToken(
    instance: AxiosInstance,
    requestConfig: InternalAxiosRequestConfig
): Promise<Api.Result> {
    return new Promise<Api.Result>((resolve) => {
        //排队等待token刷新完成后，重新发起请求
        refreshState.queue.push(() => {
            instance.request(requestConfig).then((res) => {
                resolve(res as unknown as Api.Result)
            })
        })

        if (!refreshState.isRefreshing) {
            refreshState.isRefreshing = true
            executeRefreshToken().then((refreshResult) => {
                if (refreshResult?.error) {
                    //失败了
                    resolve(refreshResult)
                } else {
                    refreshState.queue.map((cb) => cb())
                }
                refreshState.queue = []
                refreshState.isRefreshing = false
            })
        }
    })
}

/**
 * 拦截响应，只有响应码是非4xx 5xx才会走这里
 * @param instance
 */
function interceptResponse(instance: AxiosInstance) {
    instance.interceptors.response.use(async function (response: AxiosResponse) {
        const {
            headers,
            data: resData,
            config: requestConfig
        }: {
            headers: RawAxiosResponseHeaders | AxiosResponseHeaders
            config: InternalAxiosRequestConfig & Api.RequestConfig
            data: any
        } = response
        if (requestConfig.onResponse) {
            requestConfig.onResponse(response)
        }
        if (requestConfig.skipInterceptor || requestConfig.skipResponseInterceptor) {
            return { error: null, result: resData }
        }

        processResponseHeader(headers as AxiosResponseHeaders)

        const code = resData.code
        const data = resData.data
        const msg = resData.msg

        //token到期, 刷新token
        if (REFRESH_TOKEN_CODE.includes(code)) {
            return waitRefreshToken(instance, requestConfig)
        }

        // token失效, 重新登录
        if (RELOGIN_TOKEN_CODE.includes(code)) {
            const { resetAuthStore } = useAuthStore()
            resetAuthStore()
            return transformResultByBackendError(code, '授权已过期，需要重新登录', data)
        }

        //服务器有异常
        if (code !== 0) {
            return transformResultByBackendError(code, msg, data)
        }
        return {
            error: null,
            result: resData
        }
    } as unknown as (
        response: AxiosResponse<any, any>
    ) => Promise<AxiosResponse<any, any>>)
}

/**
 * 对axios做拦截处理
 * @param instance
 */
function intercept(instance: AxiosInstance) {
    interceptRequest(instance)
    interceptResponse(instance)
}

export { intercept }
